package example

import scala.collection.mutable.ArrayBuffer

object Main extends App {
  val ages = Seq(42, 75, 29, 64)
  println(s"The oldest person is ${ages.max}")
  println(1) // 1
  println(1 + 1) // 2
  println("Hello!") // Hello!
  println("Hello," + " world!") // Hello, world!
  val x = 1 + 1
  println(x) // 2
  var y = 1 + 1
  y = 3 // This compiles because "x" is declared with the "var" keyword.
  println(y * y) // 9
  println({
    val x = 1 + 1
    x + 1
  })
  val addOne = (x: Int) => x + 1
  println(addOne(10)) // 11
  val add = (x: Int, y: Int) => x + y
  println(add(1, 2)) // 3
  val getTheAnswer = () => 42
  println(getTheAnswer()) // 42
  def addM(x: Int, y: Int): Int = x + y
  println(addM(1, 2)) // 3
  def addThenMultiply(x: Int, y: Int)(multiplier: Int): Int = (x + y) * multiplier
  println(addThenMultiply(1, 2)(3)) //
  def name: String = System.getProperty("name")
  println("Hello, " + name + "!")
  def getSquareString(input: Double): String = {
    val square = input * input
    square.toString
  }
  println(getSquareString(2.5))
  class Greeter(prefix: String, suffix: String) {
    def greet(name: String): Unit =
      println(prefix + name + suffix)
  }
  val greeter = new Greeter("Hello, ", "!")
  greeter.greet("Scala developer") // Hello, Scala developer!
  case class Point(x: Int, y: Int)
  val point = Point(1, 2)
  val anotherPoint = Point(1, 2)
  val yetAnotherPoint = Point(2, 2)
  if (point == anotherPoint) {
    println(point + " and " + anotherPoint + " are the same.")
  } else {
    println(point + " and " + anotherPoint + " are different.")
  }
  // Point(1,2) and Point(1,2) are the same.

  if (point == yetAnotherPoint) {
    println(point + " and " + yetAnotherPoint + " are the same.")
  } else {
    println(point + " and " + yetAnotherPoint + " are different.")
  }
  // Point(1,2) and Point(2,2) are different.
  object IdFactory {
    private var counter = 0
    def create(): Int = {
      counter += 1
      counter
    }
  }
  val newId: Int = IdFactory.create()
  println(newId) // 1
  val newerId: Int = IdFactory.create()
  println(newerId) // 2

  trait Greeter1 {
    def greet(name: String): Unit =
      println("Hello, " + name + "!")
  }
  class DefaultGreeter extends Greeter1

  class CustomizableGreeter(prefix: String,postfix: String) extends Greeter1 {
    override def greet(name: String): Unit = {
      println(prefix + name + postfix)
    }
  }

  val list: List[Any] = List(
    "a string",
    732,  // an integer
    'c',  // a character
    true, // a boolean value
    () => "an anonymous function returning a string"
  )

  list.foreach(element => println(element))

  val x1: Long = 987654321
  val y1: Float = x1  // 9.8765434E8 (note that some precision is lost in this case)
 println(y1)
  val face: Char = '☺'
  val number: Int = face  // 97
  println(number)

  class Point1(var x: Int, var y: Int) {

    def move(dx: Int, dy: Int): Unit = {
      x = x + dx
      y = y + dy
    }

    override def toString: String =
      s"($x, $y)"
  }

  val point1 = new Point1(2, 3)
  point1.x  // 2
  println(point1)  // prints (2, 3)

  class Point2 {
    private var _x = 0
    private var _y = 0
    private val bound = 100

    def x = _x
    def x_= (newValue: Int): Unit = {
      if (newValue < bound) _x = newValue else printWarning
    }

    def y = _y
    def y_= (newValue: Int): Unit = {
      if (newValue < bound) _y = newValue else printWarning
    }

    private def printWarning = println("WARNING: Out of bounds")
  }

  val point12 = new Point2
  point1.x = 99
  point1.y = 101
  trait Iterator[A] {
    def hasNext: Boolean
    def next(): A
  }

  class IntIterator(to: Int) extends Iterator[Int] {
    private var current = 0

    override def hasNext: Boolean = current < to

    override def next(): Int = {
      if (hasNext) {
        val t = current
        current + 1
      } else 0
    }
  }

  val iterator = new IntIterator(10)
  println(iterator.next())
  println(iterator.next())

  trait Pet {
    val name: String
  }

  class Cat(val name: String) extends Pet
  class Dog(val name: String) extends Pet

  val dog = new Dog("Harry")
  val cat = new Cat("Sally")

  val animals = ArrayBuffer.empty[Pet]
  animals.append(dog)
  animals.append(cat)
  animals.foreach(pet => println(pet.name))

  abstract class A {
    val message: String
  }

  class B extends A {
    val message = "I'm an instance of class B"
  }

  trait C extends A {
    def loudMessage = message.toUpperCase()
  }

  class D extends B with C

  val d = new D
  println(d.message)
  println(d.loudMessage)

  abstract class AbsIterator {
    type T
    def hasNext: Boolean
    def next(): T
  }

  class StringIterator(s: String) extends AbsIterator {
    type T = Char
    private var i = 0
    def hasNext = i < s.length
    def next = {
      val ch = s charAt i
      i += 1
      ch
    }
  }

  trait RichIterator extends AbsIterator {
    def foreach(f: T=> Unit): Unit = while (hasNext) f(next())
  }

  val salaries = Seq(20000, 70000, 40000)
  val doubleSalary = (x: Int) => x * 2
  val newSalaries = salaries.map(doubleSalary)
  println(newSalaries)
  val newSalaries2 = salaries.map(x => x * 3)
  println(newSalaries2)
  val newSalaries3 = salaries.map(_ * 4)
  println(newSalaries3)


}
